package com.lixueju.security.box.web.authentication;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lixueju.security.box.core.dto.ResultData;
import com.lixueju.security.box.core.dto.SecurityBoxUtils;
import com.lixueju.security.box.core.model.User;
import com.lixueju.security.box.core.properties.OAuth2ClientProperties;
import com.lixueju.security.box.core.properties.SecurityBoxProperties;
import com.lixueju.security.box.web.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author lixueju
 * @since 2019/11/6 9:50
 **/
@Component("securityBoxAuthenticationSuccessHandler")
public class SecurityBoxAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    private Logger logger = LoggerFactory.getLogger(getClass());


    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthorizationServerTokenServices tokenServices;

    @Autowired
    private SecurityBoxProperties securityBoxProperties;

    @Autowired
    private SecurityBoxUtils securityBoxUtils;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IUserService userService;

    /*
     * (non-Javadoc)
     *
     * @see org.springframework.security.web.authentication.
     * AuthenticationSuccessHandler#onAuthenticationSuccess(javax.servlet.http.
     * HttpServletRequest, javax.servlet.http.HttpServletResponse,
     * org.springframework.security.core.Authentication)
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        logger.info("登录成功");
        // 删除了basic认证
        /*String header = request.getHeader("Authorization");
        if (header == null || !header.startsWith("Basic ")) {
            throw new UnapprovedClientAuthenticationException("请求头中无client信息");
        }

        String[] tokens = extractAndDecodeHeader(header, request);
        assert tokens.length == 2;
        String clientId = tokens[0];
        String clientSecret = tokens[1];*/

        OAuth2ClientProperties client = securityBoxProperties.getOauth2().getClient();
        String clientId = client.getClientId();
        String clientSecret = client.getClientSecret();

        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

        if (clientDetails == null) {
            throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在:" + clientId);
        } else if (!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)) {
            throw new UnapprovedClientAuthenticationException("clientSecret不匹配:" + clientId);
        }

        TokenRequest tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), "security_box");

        OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);

        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);

        OAuth2AccessToken token = tokenServices.createAccessToken(oAuth2Authentication);
        UserJwt userJwt = (UserJwt) authentication.getPrincipal();
        userJwt.setJti(token.getAdditionalInformation().get("jti").toString());
        userJwt.setJwtToken(token.getValue());
        userJwt.setRefreshToken(token.getRefreshToken().getValue());
        userJwt.setExpiresIn(client.getRefreshTokenValiditySeconds() - 3600);// 有效时间减去一小时
        //userJwt.setExpiresIn(token.getExpiresIn() - 3600);// 有效时间减去一小时
        securityBoxUtils.deleteUserToken(userJwt.getUserToken().getToken());
        userJwt.getUserToken().setToken(userJwt.getJti());
        this.saveToken(userJwt);

        // 更新token信息
        User user = new User();
        user.setId(userJwt.getUserToken().getId());
        user.setToken(userJwt.getJti());
        userService.updateUser(user);

        response.setContentType("application/json;charset=UTF-8");
        JSONObject userToken = JSONObject.parseObject(JSONObject.toJSONStringWithDateFormat(userJwt.getUserToken(), "yyyy-MM-dd"));
        response.getWriter().write(objectMapper.writeValueAsString(new ResultData<>(userToken)));
    }

    /**
     * 解码请求头
     */
    private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {

        byte[] base64Token = header.substring(6).getBytes("UTF-8");
        byte[] decoded;
        try {
            decoded = Base64.decode(base64Token);
        } catch (IllegalArgumentException e) {
            throw new BadCredentialsException("Failed to decode basic authentication token");
        }
        String token = new String(decoded, "UTF-8");
        int delim = token.indexOf(":");
        if (delim == -1) {
            throw new BadCredentialsException("Invalid basic authentication token");
        }
        return new String[]{token.substring(0, delim), token.substring(delim + 1)};
    }

    private boolean saveToken(UserJwt userJwt) {
        String key = securityBoxProperties.getClient().getTokenPrefix() + userJwt.getUserToken().getToken();
        stringRedisTemplate.boundValueOps(key).set(JSONObject.toJSONString(userJwt), userJwt.getExpiresIn(), TimeUnit.SECONDS);
        Long expire = stringRedisTemplate.getExpire(key, TimeUnit.SECONDS);
        return expire > 0;
    }


}
